﻿#region Disclaimer/Info
///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaCollection
// 
// MetaCollection is a system to provide a standard structure, permissions and metadata service
// for content organization.
//
// This project is hosted at Google Code at http://code.google.com/p/metacollection/
//
// This project is licensed under the BSD license.  See the License.txt file for more information.
///////////////////////////////////////////////////////////////////////////////////////////////////
#endregion

using System;
using System.Security;
using System.Web;
using MetaCollection.ContentService.Http;
using MetaCollection.Framework;
using MetaCollection.ContentService.WebDav.Methods;

namespace MetaCollection.ContentService
{
	public sealed class ContentHandler : IHttpHandler
	{
		/// <summary>
		/// Instance of the request handler associated with this httphandler.
		/// </summary>
		private IRequestHandler m_Handler;

		/// <summary>
		/// Initializes a new instance of the <see cref="ContentHandler"/> class.
		/// </summary>
		public ContentHandler()
		{
			m_Handler = RequestHandlerFactory.CreateRequestHandler();
		}

		/// <summary>
		/// Initializes a new instance of the <see cref="ContentHandler"/> class.
		/// </summary>
		/// <param name="itemRequestHandler">The item request handler.</param>
		public ContentHandler(IRequestHandler itemRequestHandler)
		{
			m_Handler = itemRequestHandler;
		}


		/// <summary>
		/// Gets a value indicating whether another request can use the <see cref="T:System.Web.IHttpHandler"/> instance.
		/// </summary>
		/// <value></value>
		/// <returns>true if the <see cref="T:System.Web.IHttpHandler"/> instance is reusable; otherwise, false.
		/// </returns>
		public bool IsReusable
		{
			get { return true; }
		}

		/// <summary>
		/// Processes the request.
		/// </summary>
		/// <param name="context">The context.</param>
		void IHttpHandler.ProcessRequest(HttpContext context)
		{
			if (context == null)
				throw new ArgumentNullException("context");
			try
			{
				try
				{
					WebDav.DavContext webDavContext = new WebDav.DavContext(context);

					switch (webDavContext.Request.Method)
					{
						case Http.HttpMethods.Get: Get.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Put: Put.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Copy: Copy.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Move: Move.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Delete: Delete.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Head: Head.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Options: Options.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Post: Post.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Search: Search.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.PropFind: PropFind.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.PropPatch: PropPatch.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.MKCol: MakeCollection.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.Report: Report.Respond(webDavContext, m_Handler); break;
						case Http.HttpMethods.VersionControl: webDavContext.Response.SetStatus((int)Http.SuccessStatus.NoContent, ""); break;
						// TODO: support Acl, CheckIn, CheckOut, and Merge methods.
						case Http.HttpMethods.Lock:
						case Http.HttpMethods.Unlock:
						default: throw new Http.ServerException(Http.ServerErrorStatus.NotImplemented);
					}
				}
				catch (SecurityException sec)
				{
					if (context.User.Identity.IsAuthenticated && !String.IsNullOrEmpty(context.User.Identity.AuthenticationType))
						throw new Http.ClientException(DetermineSubStatusCode(sec), sec.Message, sec);
					else
						throw new Http.ClientException(ClientErrorStatus.Unauthorized);
				}
			}
			catch (HttpException ex)
			{
				RenderHttpException(context.Response, ex);
			}
		}

		/// <summary>
		/// Renders the HTTP exception for the client
		/// </summary>
		/// <param name="response">The HTTP Response to use for output.</param>
		/// <param name="ex">The ex.</param>
		private static void RenderHttpException(HttpResponse response, HttpException ex)
		{
			response.Clear();
			response.StatusCode = ex.GetHttpCode();
			Http.ClientException hce = ex as Http.ClientException;
			if (hce != null)
				response.SubStatusCode = hce.SubCode;
			response.Write(ex.GetHtmlErrorMessage());
		}

		/// <summary>
		/// Determines the sub status code.
		/// </summary>
		/// <param name="sec">The sec.</param>
		/// <returns></returns>
		private static Http.SubStatus403 DetermineSubStatusCode(SecurityException sec)
		{
			Http.SubStatus403 code = Http.SubStatus403.None;
			if (!String.IsNullOrEmpty(sec.PermissionState))
			{
				if (sec.PermissionState.Contains("Read"))
					code = Http.SubStatus403.ReadDenied;
				else if (sec.PermissionState.Contains("Write"))
					code = Http.SubStatus403.WriteDenied;
			}
			return code;
		}
	}
}
